home *** CD-ROM | disk | FTP | other *** search
Text File | 2002-06-19 | 34.0 KB | 1,483 lines |
- // Copyright (C) 2001-2002 Raven Software, Inc.
- //
- // cg_players.c -- handle the media and animation for player entities
-
- #include "cg_local.h"
- #include "..\ghoul2\g2.h"
- #include "..\game\inv.h"
-
- typedef struct SCustomSound
- {
- const char *mGroup;
- const int mIndex;
- } TCustomSound;
-
- TCustomSound CustomSounds[MAX_CUSTOM_SOUNDS] =
- {
- { "Death", 0 },
- { "Death", 1 },
- { "Death", 2 },
- { "Pain", 0 },
- { "Pain", 1 },
- { "Pain", 2 },
- };
-
- /*
- ================
- CG_CustomSound
- ================
- */
- sfxHandle_t CG_CustomSound(int clientNum, const char *soundName)
- {
- return trap_S_RegisterSound( soundName );
- }
-
- /*
- ================
- CG_CustomPlayerSound
- ================
- */
- sfxHandle_t CG_CustomPlayerSound(int clientNum, ECustomSounds sound)
- {
- clientInfo_t *ci;
-
- if ( clientNum < 0 || clientNum >= MAX_CLIENTS )
- {
- clientNum = 0;
- }
- ci = &cgs.clientinfo[ clientNum ];
-
- return ci->sounds[sound];
- }
-
-
- /*
- ==========================
- CG_RegisterClientIdentity
- ==========================
- */
- static qboolean CG_RegisterClientIdentity ( clientInfo_t *ci, const char *identityName )
- {
- char afilename[MAX_QPATH];
- TIdentity* identity;
-
- // Find the identity in question
- identity = BG_FindIdentity( identityName );
- if (!identity )
- {
- return qfalse;
- }
-
- // Register the identity
- ci->ghoul2Model = CG_RegisterIdentity( identity, afilename, &ci->gender );
- if (!ci->ghoul2Model)
- {
- return qfalse;
- }
-
- // find the bolt point for hanging world models of weapons on
- ci->boltWorldWeapon = trap_G2API_AddBolt(ci->ghoul2Model, 0, "rhang_tag_bone");
-
- if ( !BG_ParseAnimationFile( afilename, ci->animations ) )
- {
- Com_Printf( "Failed to load animation file %s\n", afilename );
- trap_G2API_RemoveGhoul2Model(&ci->ghoul2Model, 0);
- return qfalse;
- }
-
- // Flag model as male or female.
- assert(afilename);
- ci->isMale=(strstr(afilename,"female"))?qfalse:qtrue;
- ci->identity = identity;
-
- return qtrue;
- }
-
- /*
- ====================
- CG_ColorFromString
- ====================
- */
- static void CG_ColorFromString( const char *v, vec3_t color ) {
- int val;
-
- VectorClear( color );
-
- val = atoi( v );
-
- if ( val < 1 || val > 7 ) {
- VectorSet( color, 1, 1, 1 );
- return;
- }
-
- if ( val & 1 ) {
- color[2] = 1.0f;
- }
- if ( val & 2 ) {
- color[1] = 1.0f;
- }
- if ( val & 4 ) {
- color[0] = 1.0f;
- }
- }
-
- /*
- ===================
- CG_LoadClientInfo
-
- Load it now, taking the disk hits.
- This will usually be deferred to a safe time
- ===================
- */
- static void CG_LoadClientInfo( clientInfo_t *ci )
- {
- int i;
- int modelloaded;
- int clientNum;
-
- modelloaded = qtrue;
-
- // Initialize all bolt positions
- ci->boltWorldWeapon = -1;
- ci->boltNightvision = -1;
- for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
- {
- ci->boltGametypeItems[i] = -1;
- }
-
- // Register the client identity
- if ( !CG_RegisterClientIdentity ( ci, ci->identityName ) )
- {
- if ( !CG_RegisterClientIdentity ( ci, DEFAULT_IDENTITY ) )
- {
- Com_Error( ERR_FATAL, "DEFAULT_IDENTITY (%s) failed to register", DEFAULT_IDENTITY );
- }
- }
-
- // sounds
- for( i=0; i< MAX_CUSTOM_SOUNDS; i++ )
- {
- const char* sound;
-
- ci->sounds[i] = 0;
- sound = NULL;
-
- sound = BG_GetModelSound ( ci->identityName, CustomSounds[i].mGroup, CustomSounds[i].mIndex );
-
- // If there is a valid sound to load then load it
- if ( sound && sound[0] )
- {
- ci->sounds[i] = trap_S_RegisterSound( sound );
- }
- }
-
- ci->deferred = qfalse;
-
- // reset any existing players and bodies, because they might be in bad
- // frames for this new model
- clientNum = ci - cgs.clientinfo;
- for ( i = 0 ; i < MAX_GENTITIES ; i++ )
- {
- centity_t* cent;
-
- cent = CG_GetEntity ( i );
-
- if ( cent->currentState.clientNum == clientNum &&
- cent->currentState.eType == ET_PLAYER )
- {
- CG_ResetPlayerEntity( cent );
- }
- }
- }
-
- /*
- ======================
- CG_CopyClientInfoModel
- ======================
- */
- static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to )
- {
- int i;
-
- to->gender = from->gender;
-
- to->boltWorldWeapon = from->boltWorldWeapon;
-
- for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
- {
- to->boltGametypeItems[i] = from->boltGametypeItems[i];
- }
-
- to->boltNightvision = from->boltNightvision;
-
- // Copy all the ghoul2 information too
- if (from->ghoul2Model)
- {
- trap_G2API_DuplicateGhoul2Instance( from->ghoul2Model, &to->ghoul2Model );
- }
- else
- {
- assert(0);
- Com_Error(ERR_DROP, "CG_CopyClientInfoModel invalid g2 pointer\n");
- }
-
- memcpy( to->animations, from->animations, sizeof( to->animations ) );
- memcpy( to->sounds, from->sounds, sizeof( to->sounds ) );
- }
-
- /*
- ======================
- CG_ScanForExistingClientInfo
- ======================
- */
- static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci )
- {
- int i;
- clientInfo_t *match;
-
- for ( i = 0 ; i < cgs.maxclients ; i++ )
- {
- match = &cgs.clientinfo[ i ];
-
- if ( !match->infoValid )
- {
- continue;
- }
- if ( match->deferred )
- {
- continue;
- }
-
- // Identity name doesnt match
- if ( Q_stricmp ( ci->identityName, match->identityName ) )
- {
- continue;
- }
-
- if ( ci->team == TEAM_FREE ||
- ci->team == TEAM_SPECTATOR ||
- ci->team == match->team )
- {
- // this clientinfo is identical, so use it's handles
- ci->deferred = qfalse;
-
- CG_CopyClientInfoModel( match, ci );
-
- return qtrue;
- }
- }
-
- // nothing matches, so defer the load
- return qfalse;
- }
-
- /*
- ======================
- CG_SetDeferredClientInfo
-
- We aren't going to load it now, so grab some other
- client's info to use until we have some spare time.
- ======================
- */
- static void CG_SetDeferredClientInfo( clientInfo_t *ci )
- {
- int i;
- clientInfo_t *match;
-
- // spectators can use any skin
- if ( ci->team == TEAM_SPECTATOR )
- {
- for ( i = 0; i < cgs.maxclients; i ++ )
- {
- match = &cgs.clientinfo[ i ];
- if ( !match->infoValid )
- {
- continue;
- }
-
- ci->deferred = qtrue;
- CG_CopyClientInfoModel( match, ci );
- return;
- }
- }
-
- // Try to use any skin from the team they are joining
- if ( cgs.gametypeData->teams )
- {
- for ( i = 0; i < cgs.maxclients; i ++ )
- {
- match = &cgs.clientinfo[ i ];
- if ( !match->infoValid )
- {
- continue;
- }
-
- // Must be on the same team
- if ( match->team != ci->team )
- {
- continue;
- }
-
- ci->deferred = qtrue;
- CG_CopyClientInfoModel( match, ci );
- return;
- }
- }
- // find the first valid clientinfo and grab its stuff
- else
- {
- for ( i = 0 ; i < cgs.maxclients ; i++ )
- {
- match = &cgs.clientinfo[ i ];
- if ( !match->infoValid )
- {
- continue;
- }
-
- ci->deferred = qtrue;
- CG_CopyClientInfoModel( match, ci );
- return;
- }
- }
-
- CG_LoadClientInfo( ci );
- }
-
- /*
- ======================
- CG_ResetClientIdentity
- ======================
- */
- void CG_ResetClientIdentity ( int clientNum )
- {
- clientInfo_t *ci;
- centity_t *cent;
- int i;
-
- ci = &cgs.clientinfo[clientNum];
- cent = CG_GetEntity ( clientNum );
-
- // Clean up the raw ghoul model
- if ( ci->ghoul2Model )
- {
- trap_G2API_CleanGhoul2Models ( &ci->ghoul2Model );
- ci->ghoul2Model = NULL;
- }
-
- // Clean up the active ghoul model
- if ( cent->ghoul2 )
- {
- trap_G2API_CleanGhoul2Models ( ¢->ghoul2 );
- cent->pe.weaponModelSpot = 0;
- cent->pe.weapon = 0;
- cent->ghoul2 = NULL;
- }
-
- // Reset all bolt position
- ci->boltWorldWeapon = -1;
- ci->boltNightvision = -1;
- for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
- {
- ci->boltGametypeItems[i] = -1;
- }
- }
-
- /*
- ======================
- CG_NewClientInfo
- ======================
- */
- void CG_NewClientInfo( int clientNum )
- {
- clientInfo_t *ci;
- clientInfo_t newInfo;
- centity_t* cent;
- const char *configstring;
- const char *v;
-
- ci = &cgs.clientinfo[clientNum];
-
- cent = CG_GetEntity ( clientNum );
-
- configstring = CG_ConfigString( clientNum + CS_PLAYERS );
- if ( !configstring[0] )
- {
- memset( ci, 0, sizeof( *ci ) );
- return;
- }
-
- // Clear the client models / etc
- CG_ResetClientIdentity ( clientNum );
-
- ci->infoValid = qfalse;
-
- // build into a temp buffer so the defer checks can use
- // the old value
- memset( &newInfo, 0, sizeof( newInfo ) );
-
- // isolate the player's name
- v = Info_ValueForKey(configstring, "n");
- Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
-
- // bot skill
- v = Info_ValueForKey( configstring, "skill" );
- newInfo.botSkill = atoi( v );
-
- // handicap
- v = Info_ValueForKey( configstring, "hc" );
- newInfo.handicap = atoi( v );
-
- // wins
- v = Info_ValueForKey( configstring, "w" );
- newInfo.wins = atoi( v );
-
- // losses
- v = Info_ValueForKey( configstring, "l" );
- newInfo.losses = atoi( v );
-
- // team
- v = Info_ValueForKey( configstring, "t" );
- newInfo.team = atoi( v );
-
- // team task
- v = Info_ValueForKey( configstring, "tt" );
- newInfo.teamTask = atoi(v);
-
- // team leader
- v = Info_ValueForKey( configstring, "tl" );
- newInfo.teamLeader = atoi(v);
-
- // identity
- v = Info_ValueForKey( configstring, "identity" );
- Q_strncpyz ( newInfo.identityName, v, sizeof(newInfo.identityName) );
-
- // Inform the ui of the team switch
- if ( clientNum == cg.clientNum )
- {
- char temp[MAX_QPATH];
- const char* identityCvar;
-
- trap_Cvar_Set ( "ui_info_team", va("%i",newInfo.team ) );
-
- if (cgs.gametypeData->teams )
- {
- identityCvar = "team_identity";
- }
- else
- {
- identityCvar = "identity";
- }
-
- trap_Cvar_VariableStringBuffer ( identityCvar, temp, sizeof(temp) );
-
- if ( Q_stricmp ( temp, newInfo.identityName ) )
- {
- trap_Cvar_Set ( identityCvar, newInfo.identityName );
- }
- }
-
- // scan for an existing clientinfo that matches this modelname
- // so we can avoid loading checks if possible
- if ( !CG_ScanForExistingClientInfo( &newInfo ) )
- {
- qboolean forceDefer;
-
- forceDefer = trap_MemoryRemaining() < 4000000;
-
- // if we are defering loads, just have it pick the first valid
- if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) )
- {
- // keep whatever they had if it won't violate team skins
- CG_SetDeferredClientInfo( &newInfo );
-
- // if we are low on memory, leave them with this model
- if ( forceDefer )
- {
- Com_Printf( "Memory is low. Using deferred model.\n" );
- newInfo.deferred = qfalse;
- }
- }
- else
- {
- CG_LoadClientInfo( &newInfo );
- }
- }
-
- // replace whatever was there with the new one
- newInfo.infoValid = qtrue;
- *ci = newInfo;
- }
-
- /*
- ======================
- CG_LoadDeferredPlayers
-
- Called each frame when a player is dead
- and the scoreboard is up
- so deferred players can be loaded
- ======================
- */
- void CG_LoadDeferredPlayers( void )
- {
- int i;
- clientInfo_t *ci;
-
- // scan for a deferred player to load
- for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ )
- {
- if ( ci->infoValid && ci->deferred )
- {
- // if we are low on memory, leave it deferred
- if ( trap_MemoryRemaining() < 4000000 )
- {
- Com_Printf( "Memory is low. Using deferred model.\n" );
- ci->deferred = qfalse;
- continue;
- }
-
- // Clear the client models / etc
- CG_ResetClientIdentity ( i );
-
- CG_LoadClientInfo( ci );
- }
- }
- }
-
- /*
- =================
- CG_AddPainTwitch
- =================
- */
- static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles )
- {
- int t;
- float f;
-
- t = cg.time - cent->pe.painTime;
- if ( t >= PAIN_TWITCH_TIME ) {
- return;
- }
-
- f = 1.0 - (float)t / PAIN_TWITCH_TIME;
-
- if ( cent->pe.painDirection ) {
- torsoAngles[ROLL] += 20 * f;
- } else {
- torsoAngles[ROLL] -= 20 * f;
- }
- }
-
- /*
- ===============
- CG_PlayerFloatSprite
-
- Float a sprite over the player's head
- ===============
- */
- static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader )
- {
- int rf;
- refEntity_t ent;
-
- if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson )
- {
- // only show in mirrors
- rf = RF_THIRD_PERSON;
- }
- else
- {
- rf = 0;
- }
-
- memset( &ent, 0, sizeof( ent ) );
- VectorCopy( cent->lerpOrigin, ent.origin );
- ent.origin[2] += 58;
- ent.reType = RT_SPRITE;
- ent.customShader = shader;
- ent.radius = 10;
- ent.renderfx = rf;
- ent.shaderRGBA[0] = 255;
- ent.shaderRGBA[1] = 255;
- ent.shaderRGBA[2] = 255;
- ent.shaderRGBA[3] = 255;
- trap_R_AddRefEntityToScene( &ent );
- }
-
- /*
- ===============
- CG_PlayerSprites
-
- Float sprites over the player's head
- ===============
- */
- static void CG_PlayerSprites( centity_t *cent )
- {
- int team;
-
- team = cgs.clientinfo[ cent->currentState.clientNum ].team;
- if ( !(cent->currentState.number == cg.snap->ps.clientNum) &&
- !(cent->currentState.eFlags & EF_DEAD) &&
- cg.snap->ps.persistant[PERS_TEAM] == team &&
- cgs.gametypeData->teams )
- {
- if (cg_drawFriend.integer)
- {
- CG_PlayerFloatSprite( cent, team==TEAM_RED?cgs.media.redFriendShader:cgs.media.blueFriendShader );
- }
-
- return;
- }
- }
-
- /*
- ===============
- CG_PlayerShadow
-
- Returns the Z component of the surface being shadowed
-
- should it return a full plane instead of a Z?
- ===============
- */
- #define SHADOW_DISTANCE 256
- qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane )
- {
- vec3_t end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
- trace_t trace;
- float alpha;
-
- *shadowPlane = 0;
-
- if ( cg_shadows.integer == 0 ) {
- return qfalse;
- }
-
- // send a trace down from the player to the ground
- VectorCopy( cent->lerpOrigin, end );
- end[2] -= SHADOW_DISTANCE;
-
- trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
-
- // no shadow if too high
- if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) {
- return qfalse;
- }
-
- *shadowPlane = trace.endpos[2] + 1;
-
- if ( cg_shadows.integer != 1 ) { // no mark for stencil or projection shadows
- return qtrue;
- }
-
- // fade the shadow out with height
- alpha = 1.0 - trace.fraction;
- alpha = 1.0;
- trap_R_AddDecalToScene ( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal,
- cent->currentState.apos.trBase[YAW], 1.0,0.0,1.0,1.0, qfalse, 24, qtrue );
-
- return qtrue;
- }
-
-
- /*
- ===============
- CG_PlayerSplash
-
- Draw a mark at the water surface
- ===============
- */
- static void CG_PlayerSplash( centity_t *cent ) {
- vec3_t start, end;
- trace_t trace;
- int contents;
- polyVert_t verts[4];
-
- if ( !cg_shadows.integer ) {
- return;
- }
-
- VectorCopy( cent->lerpOrigin, end );
- end[2] -= 24;
-
- // if the feet aren't in liquid, don't make a mark
- // this won't handle moving water brushes, but they wouldn't draw right anyway...
- contents = trap_CM_PointContents( end, 0 );
- if ( !( contents & ( CONTENTS_WATER | CONTENTS_LAVA ) ) ) {
- return;
- }
-
- VectorCopy( cent->lerpOrigin, start );
- start[2] += 32;
-
- // if the head isn't out of liquid, don't make a mark
- contents = trap_CM_PointContents( start, 0 );
- if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_LAVA ) ) {
- return;
- }
-
- // trace down to find the surface
- trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_LAVA ) );
-
- if ( trace.fraction == 1.0 ) {
- return;
- }
-
- // create a mark polygon
- VectorCopy( trace.endpos, verts[0].xyz );
- verts[0].xyz[0] -= 32;
- verts[0].xyz[1] -= 32;
- verts[0].st[0] = 0;
- verts[0].st[1] = 0;
- verts[0].modulate[0] = 255;
- verts[0].modulate[1] = 255;
- verts[0].modulate[2] = 255;
- verts[0].modulate[3] = 255;
-
- VectorCopy( trace.endpos, verts[1].xyz );
- verts[1].xyz[0] -= 32;
- verts[1].xyz[1] += 32;
- verts[1].st[0] = 0;
- verts[1].st[1] = 1;
- verts[1].modulate[0] = 255;
- verts[1].modulate[1] = 255;
- verts[1].modulate[2] = 255;
- verts[1].modulate[3] = 255;
-
- VectorCopy( trace.endpos, verts[2].xyz );
- verts[2].xyz[0] += 32;
- verts[2].xyz[1] += 32;
- verts[2].st[0] = 1;
- verts[2].st[1] = 1;
- verts[2].modulate[0] = 255;
- verts[2].modulate[1] = 255;
- verts[2].modulate[2] = 255;
- verts[2].modulate[3] = 255;
-
- VectorCopy( trace.endpos, verts[3].xyz );
- verts[3].xyz[0] += 32;
- verts[3].xyz[1] -= 32;
- verts[3].st[0] = 1;
- verts[3].st[1] = 0;
- verts[3].modulate[0] = 255;
- verts[3].modulate[1] = 255;
- verts[3].modulate[2] = 255;
- verts[3].modulate[3] = 255;
-
- trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
- }
-
- /*
- ===============
- CG_UpdatePlayerAnimations
- ===============
- */
- static void CG_UpdatePlayerAnimations ( centity_t* cent )
- {
- clientInfo_t *ci;
-
- // Make sure its a player
- assert ( cent->currentState.eType == ET_PLAYER );
-
- // Retrieve the client info pointer for the given client
- ci = &cgs.clientinfo[ cent->currentState.clientNum ];
-
- // Update the leg animation
- if ( cent->pe.legs.anim != cent->currentState.legsAnim && cent->currentState.legsAnim != -1 )
- {
- animation_t *anim;
- int flags;
- float speed;
-
- anim = &ci->animations[ (cent->currentState.legsAnim & (~ANIM_TOGGLEBIT)) ];
-
- if ( anim->loopFrames == -1 )
- {
- flags = BONE_ANIM_OVERRIDE|BONE_ANIM_OVERRIDE_FREEZE;
- }
- else
- {
- flags = BONE_ANIM_OVERRIDE_LOOP;
- }
-
- if (cg_animBlend.integer)
- {
- flags |= BONE_ANIM_BLEND;
- }
-
- speed = 1.0f;
-
- trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, 50.0f / anim->frameLerp * speed, cg.time, -1, 150);
-
- cent->pe.legs.anim = cent->currentState.legsAnim;
- cent->pe.legs.animTime = cg.time;
- }
-
- // Update the torso animation
- if ( cent->pe.torso.anim != cent->currentState.torsoAnim && cent->currentState.torsoAnim != -1 )
- {
- animation_t *anim;
- int flags;
- float time;
- anim = &ci->animations[ (cent->currentState.torsoAnim & (~ANIM_TOGGLEBIT)) ];
-
- if ( anim->loopFrames == -1 )
- {
- flags = BONE_ANIM_OVERRIDE|BONE_ANIM_OVERRIDE_FREEZE;
- }
- else
- {
- flags = BONE_ANIM_OVERRIDE_LOOP;
- }
-
- if (cg_animBlend.integer)
- {
- flags |= BONE_ANIM_BLEND;
- }
-
- if ( cent->currentState.torsoTimer > 0 )
- {
- time = cent->currentState.torsoTimer;
- time /= anim->numFrames;
- }
- else
- {
- time = anim->frameLerp;
- }
-
- // Default to 20 FPS.
- if ( time <= 0 )
- {
- time = 1000.0f / 20.0f;
- }
-
- trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, 50.0f / time, cg.time, -1, 150);
-
- cent->pe.torso.anim = cent->currentState.torsoAnim ;
- cent->pe.torso.animTime = cg.time;
- }
- }
-
- /*
- ===============
- CG_PlayerGametypeItems
- ===============
- */
- void CG_PlayerGametypeItems ( centity_t* cent, vec3_t angles )
- {
- clientInfo_t *ci = &cgs.clientinfo[cent->currentState.clientNum];
- int i;
- int rf;
-
- if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson )
- {
- // only show in mirrors
- rf = RF_THIRD_PERSON;
- }
- else
- {
- rf = 0;
- }
-
- // If the player is wearing goggles then draw them on their head
- if ( cent->currentState.eFlags & EF_GOGGLES )
- {
- refEntity_t item;
-
- if ( ci->boltNightvision == -1 )
- {
- ci->boltNightvision = trap_G2API_AddBolt( cent->ghoul2, 0, "*head_t" );
- }
-
- memset ( &item, 0, sizeof(item) );
- item.renderfx = rf | RF_MINLIGHT;
- item.ghoul2 = cgs.media.nightVisionModel;
- G2_PositionEntityOnBolt ( &item, cent->ghoul2, 0, ci->boltNightvision, cent->lerpOrigin, angles, cent->modelScale );
- trap_R_AddRefEntityToScene(&item);
- }
-
-
- // Loop through the gametye items and handle each separately
- for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
- {
- // If they dont have the item then skip it
- if ( !(cent->currentState.gametypeitems & (1<<i) ))
- {
- continue;
- }
-
- // Need to add bolt to model
- if ( cg_items[ MODELINDEX_GAMETYPE_ITEM + i ].boltModel )
- {
- refEntity_t item;
-
- if ( ci->boltGametypeItems[i] == -1 )
- {
- ci->boltGametypeItems[i] = trap_G2API_AddBolt( cent->ghoul2, 0, "*back" );
- if ( ci->boltGametypeItems[i] == -1 )
- {
- continue;
- }
-
- // Remove all identity items on the back
- CG_RemoveIdentityItemsOnBack ( cent );
- }
-
- memset ( &item, 0, sizeof(item) );
- item.renderfx = rf | RF_MINLIGHT;
- item.ghoul2 = cg_items[ MODELINDEX_GAMETYPE_ITEM + i ].boltModel;
- G2_PositionEntityOnBolt ( &item, cent->ghoul2, 0, ci->boltGametypeItems[i], cent->lerpOrigin, angles, cent->modelScale );
- trap_R_AddRefEntityToScene(&item);
- }
- }
- }
-
- /*
- ===============
- CG_UpdatePlayerWeaponModel
- ===============
- */
- void CG_UpdatePlayerWeaponModel ( centity_t* cent, qboolean force )
- {
- clientInfo_t *ci = &cgs.clientinfo[cent->currentState.clientNum];
- void *model;
-
- model = NULL;
- if ( (cent->pe.torso.anim&~ANIM_TOGGLEBIT) == TORSO_USE )
- {
- int item;
-
- cent->pe.weapon = -1;
-
- for ( item = 0; item < MAX_GAMETYPE_ITEMS; item ++ )
- {
- if ( cent->currentState.gametypeitems & (1<<item) )
- {
- model = cg_items[MODELINDEX_GAMETYPE_ITEM+item].useModel;
- break;
- }
- }
- }
- // If the weapon model hasnt changed then dont update it
- else if ( !force && cent->currentState.weapon == cent->pe.weapon )
- {
- return;
- }
- else
- {
- // Ensure the weapon is registered
- CG_RegisterWeapon ( cent->currentState.weapon );
-
- model = cg_weapons[cent->currentState.weapon].weaponG2Model;
-
- cent->pe.weapon = cent->currentState.weapon;
- }
-
- // Get rid of whats in their hand
- if ( cent->pe.weaponModelSpot )
- {
- trap_G2API_DetachG2Model ( cent->ghoul2, cent->pe.weaponModelSpot );
- trap_G2API_RemoveGhoul2Model( ¢->ghoul2, cent->pe.weaponModelSpot );
- cent->pe.weaponModelSpot = 0;
- }
-
- // If we have a ghoul model then we can attach it to the right hand of the player model
- if (model)
- {
- cent->pe.weaponModelSpot = trap_G2API_CopySpecificGhoul2Model(model, 0, cent->ghoul2, -1 );
- trap_G2API_SetBoltInfo(cent->ghoul2, cent->pe.weaponModelSpot, ci->boltWorldWeapon );
- trap_G2API_AttachG2Model(cent->ghoul2, cent->pe.weaponModelSpot, cent->ghoul2, ci->boltWorldWeapon, 0);
-
- // Special case to make sure the bayonet shows up for the ak74
- switch ( cent->currentState.weapon )
- {
- case WP_AK74_ASSAULT_RIFLE:
- trap_G2API_SetSurfaceOnOff( cent->ghoul2, cent->pe.weaponModelSpot, "bayonet_off", 0 );
- break;
-
- case WP_M4_ASSAULT_RIFLE:
- trap_G2API_SetSurfaceOnOff( cent->ghoul2, cent->pe.weaponModelSpot, "m203_off", 0 );
- break;
- }
- }
- }
-
- /*
- ===============
- CG_UpdatePlayerModel
- ===============
- */
- void CG_UpdatePlayerModel ( centity_t* cent )
- {
- if ((cent->ghoul2 == NULL) && trap_G2_HaveWeGhoul2Models(cgs.clientinfo[cent->currentState.clientNum].ghoul2Model))
- {
- CG_ResetPlayerEntity ( cent );
-
- if (!cgs.clientinfo[cent->currentState.clientNum].ghoul2Model)
- {
- Com_Error(ERR_DROP, "CG_UpdatePlayerModel invalid g2 pointer for client\n");
- }
- trap_G2API_DuplicateGhoul2Instance(cgs.clientinfo[cent->currentState.clientNum].ghoul2Model, ¢->ghoul2);
-
- // Force the players weapon model to be updated since their main model changed
- CG_UpdatePlayerWeaponModel ( cent, qtrue );
- }
- }
-
- /*
- ===============
- CG_Player
- ===============
- */
- void CG_Player( centity_t *cent )
- {
- clientInfo_t *ci;
- refEntity_t player;
- int clientNum;
- int renderfx;
- qboolean shadow;
- float shadowPlane;
- int iwantout = 0;
- vec3_t save;
-
- // Dont draw any player models when the round has ended
- if ( cg.snap->ps.pm_type == PM_INTERMISSION )
- {
- return;
- }
-
- // the client number is stored in clientNum. It can't be derived
- // from the entity number, because a single client may have
- // multiple corpses on the level using the same clientinfo
- clientNum = cent->currentState.clientNum;
- if ( clientNum < 0 || clientNum >= MAX_CLIENTS )
- {
- Com_Error( ERR_FATAL, "Bad clientNum on player entity");
- }
-
- ci = &cgs.clientinfo[ clientNum ];
-
- // it is possible to see corpses from disconnected players that may
- // not have valid clientinfo
- if ( !ci->infoValid )
- {
- return;
- }
-
- // don't draw the death sequences, as the body queue should be doing it for us
- if ( cent->currentState.eFlags & EF_DEAD )
- {
- return;
- }
-
- // Add the player to the radar if on the same team and its a team game
- if ( cgs.gametypeData->teams )
- {
- if ( cg.snap->ps.clientNum != cent->currentState.number && ci->team == cgs.clientinfo[ cg.snap->ps.clientNum ].team )
- {
- cg.radarEntities[cg.radarEntityCount++] = cent;
- }
- }
-
- // get the player model information
- renderfx = 0;
- if ( cent->currentState.number == cg.snap->ps.clientNum)
- {
- // If rendering third person then we shouldnt draw the player if
- // the view origin is too close to the player (ie, inside it)
- if ( cg.renderingThirdPerson )
- {
- vec3_t diff;
- vec3_t a;
- vec3_t b;
- float f;
-
- VectorCopy ( cg.refdef.vieworg, a );
- VectorCopy ( cent->lerpOrigin, b );
- // a[2] = 0;
- // b[2] = 0;
-
- VectorSubtract ( a, b, diff );
-
- f = VectorLengthSquared ( diff );
- if ( VectorLengthSquared ( diff ) < 1800 )
- {
- renderfx = RF_THIRD_PERSON;
- }
- }
- // only draw in mirrors
- else
- {
- renderfx = RF_THIRD_PERSON;
- }
- }
-
- // Initialize the ref entity
- memset (&player, 0, sizeof(player));
-
- // NOTENOTE Temporary
- VectorSet(player.modelScale, 1,1,1);
- player.radius = 64;
- VectorClear(player.angles);
-
- // add the shadow
- shadow = CG_PlayerShadow( cent, &shadowPlane );
-
- if (iwantout)
- {
- return;
- }
-
- // add a water splash if partially in and out of water
- // CG_PlayerSplash( cent );
-
- if ( cg_shadows.integer == 3 && shadow )
- {
- renderfx |= RF_SHADOW_PLANE;
- }
-
- // use the same origin for all
- renderfx |= RF_LIGHTING_ORIGIN | RF_MINLIGHT;
-
- VectorCopy( cent->lerpOrigin, player.origin );
- VectorCopy( cent->lerpOrigin, player.lightingOrigin );
-
- player.shadowPlane = shadowPlane;
- player.renderfx = renderfx;
-
- // don't positionally lerp at all
- VectorCopy (player.origin, player.oldorigin);
-
- // get the animation state (after rotation, to allow feet shuffle)
- CG_UpdatePlayerModel ( cent );
- CG_UpdatePlayerAnimations ( cent );
- CG_UpdatePlayerWeaponModel ( cent, qfalse );
-
- if ( (cent->currentState.eFlags & EF_DEAD ) )
- {
- AnglesToAxis( cent->lerpAngles, player.axis );
- }
- else
- {
- // Force the legs and torso to stay aligned for now to ensure the client
- // and server are in sync with the angles.
- // TODO: Come up with a way to keep these in sync on both client and server
- cent->pe.torso.yawing = qtrue;
- cent->pe.torso.pitching = qtrue;
- cent->pe.legs.yawing= qtrue;
-
- BG_PlayerAngles( cent->lerpAngles,
- player.axis,
-
- cent->pe.ghoulLegsAngles,
- cent->pe.ghoulLowerTorsoAngles,
- cent->pe.ghoulUpperTorsoAngles,
- cent->pe.ghoulHeadAngles,
-
- cent->lerpLeanOffset,
-
- cent->pe.painTime,
- cent->pe.painDirection,
- cg.time,
-
- ¢->pe.torso,
- ¢->pe.legs,
-
- cg.frametime,
- cent->lerpVelocity,
- (cent->currentState.eFlags & EF_DEAD),
- cent->currentState.angles2[YAW],
- cent->ghoul2 );
- }
-
- VectorCopy ( cent->lerpOrigin, save );
- VectorCopy ( player.origin, cent->lerpOrigin );
-
- CG_ScaleModelAxis(&player);
-
- // Copy the ghoul 2 info into the ref entity
- CG_SetGhoul2Info(&player, cent);
-
- // Now add the player to the scene
- trap_R_AddRefEntityToScene(&player);
-
- //
- // add the gun / barrel / flash
- //
- // need the angle AFTER the lean is added
- // vectoangles( player.axis[0], cent->pe.ghoulRootAngles );
-
- // Render any weapon related effects
- CG_PlayerWeaponEffects ( &player, cent, ci->team, cent->pe.ghoulLegsAngles );
-
- // Render any of the floating sprites above the players head
- CG_PlayerSprites ( cent );
-
- CG_PlayerGametypeItems ( cent, cent->pe.ghoulLegsAngles );
-
- VectorCopy ( save, cent->lerpOrigin );
- }
-
- /*
- ===============
- CG_ResetPlayerEntity
-
- A player just came into view or teleported, so reset all animation info
- ===============
- */
- void CG_ResetPlayerEntity( centity_t *cent )
- {
- cent->errorTime = -99999; // guarantee no error decay added
- cent->extrapolated = qfalse;
-
- BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
- BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
-
- VectorCopy( cent->lerpOrigin, cent->rawOrigin );
- VectorCopy( cent->lerpAngles, cent->rawAngles );
-
- cent->pe.legs.anim = -1;
- cent->pe.torso.anim = -1;
-
- memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) );
- cent->pe.legs.yawAngle = cent->rawAngles[YAW];
- cent->pe.legs.yawing = qfalse;
- cent->pe.legs.pitchAngle = 0;
- cent->pe.legs.pitching = qfalse;
-
- memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) );
- cent->pe.torso.yawAngle = cent->rawAngles[YAW];
- cent->pe.torso.yawing = qfalse;
- cent->pe.torso.pitchAngle = cent->rawAngles[PITCH];
- cent->pe.torso.pitching = qfalse;
- }
-
- /*
- =================
- CG_ProcessIdentityItems
-
- Attaches all the items for a character skin
- =================
- */
- static void CG_ProcessIdentityItems ( TGhoul2 ghoul2, TInventoryTemplate *items )
- {
- TSurfaceList *surf;
-
- while ( items )
- {
- if ( items->mItem )
- {
- if ( items->mItem->mModel && items->mBolt )
- {
- items->mModelIndex = trap_G2API_InitGhoul2Model(&ghoul2, items->mItem->mModel, 0, 0, 0, 0, 0);
- if (items->mModelIndex != -1)
- {
- items->mBoltIndex = trap_G2API_AddBolt ( ghoul2, 0, items->mBolt);
- if (items->mBoltIndex != -1)
- {
- trap_G2API_AttachG2Model( ghoul2, items->mModelIndex, ghoul2, items->mBoltIndex, 0);
- }
- }
- }
-
- surf = items->mItem->mOffList;
- while(surf)
- {
- trap_G2API_SetSurfaceOnOff( ghoul2, 0, surf->mName, G2SURFACEFLAG_OFF);
-
- surf = surf->mNext;
- }
-
- surf = items->mItem->mOnList;
- while(surf)
- {
- trap_G2API_SetSurfaceOnOff( ghoul2, 0, surf->mName, 0);
-
- surf = surf->mNext;
- }
- }
-
- items = items->mNext;
- }
- }
-
- /*
- =================
- CG_RemoveIdentityItemsOnBack
-
- Removes all identity items on the back of the given entity. There is
- currently no way to get the identity items back after removing them
- without killing off the player.
- =================
- */
- void CG_RemoveIdentityItemsOnBack ( centity_t* cent )
- {
- TInventoryTemplate* items[2];
- TIdentity* identity;
- int pass;
-
- // Grab the identity structure for this entity
- identity = cgs.clientinfo[cent->currentState.number].identity;
- if ( !identity )
- {
- return;
- }
-
- // Takes two passes to remove em all since the item lists are in two places
- items[0] = identity->mCharacter->mInventory;
- items[1] = identity->mSkin->mInventory;
-
- for ( pass = 0; pass < 2; pass ++ )
- {
- while ( items[pass] )
- {
- if ( items[pass]->mOnBack && items[pass]->mItem )
- {
- TSurfaceList *surf;
-
- // Bolt on?
- if ( items[pass]->mBolt && *items[pass]->mBolt)
- {
- trap_G2API_DetachG2Model ( cent->ghoul2, items[pass]->mModelIndex );
- trap_G2API_RemoveGhoul2Model ( ¢->ghoul2, items[pass]->mModelIndex );
- }
-
- // Surface list? Reversed from what process does
- surf = items[pass]->mItem->mOffList;
- while(surf)
- {
- trap_G2API_SetSurfaceOnOff( cent->ghoul2, 0, surf->mName, 0);
-
- surf = surf->mNext;
- }
-
- surf = items[pass]->mItem->mOnList;
- while(surf)
- {
- trap_G2API_SetSurfaceOnOff( cent->ghoul2, 0, surf->mName, G2SURFACEFLAG_OFF);
-
- surf = surf->mNext;
- }
- }
-
- items[pass] = items[pass]->mNext;
- }
- }
- }
-
- /*
- =================
- CG_RegisterIdentity
-
- Registers an identity
- =================
- */
- TGhoul2 CG_RegisterIdentity ( TIdentity* identity, char *animationFile, gender_t* gender )
- {
- char name[MAX_QPATH];
- TGenericParser2 skinFile;
- TGPGroup *basegroup, *group, *sub;
- char temp[20480], *end;
- int numPairs;
- TGhoul2 ghoul2Ptr;
-
- numPairs = 0;
- end = temp;
- *end = 0;
- ghoul2Ptr = NULL;
-
- *animationFile = 0;
-
- if (!identity )
- {
- return NULL;
- }
-
- if (trap_G2API_InitGhoul2Model( &ghoul2Ptr, identity->mCharacter->mModel, 0, 0, 0, (1<<4), 0) == -1)
- {
- return NULL;
- }
-
- trap_G2API_GetAnimFileNameIndex( ghoul2Ptr, 0, name );
- Com_sprintf(animationFile, MAX_QPATH, "%s_mp.cfg", name );
-
- if ( identity->mCharacter->mParent)
- {
- CG_ProcessIdentityItems( ghoul2Ptr, identity->mCharacter->mParent->mInventory );
- }
-
- CG_ProcessIdentityItems( ghoul2Ptr, identity->mCharacter->mInventory);
- CG_ProcessIdentityItems( ghoul2Ptr, identity->mSkin->mInventory);
-
- // don't need the mouth
- trap_G2API_SetSurfaceOnOff( ghoul2Ptr, 0, "mouth_r", G2SURFACEFLAG_OFF|G2SURFACEFLAG_NODESCENDANTS);
- trap_G2API_SetSurfaceOnOff( ghoul2Ptr, 0, "mouth_l", G2SURFACEFLAG_OFF|G2SURFACEFLAG_NODESCENDANTS);
-
- // Parse the g2skin file
- Com_sprintf( name, sizeof(name), "models/characters/skins/%s.g2skin", identity->mSkin->mSkin );
- skinFile = trap_GP_ParseFile( name, qtrue, qfalse );
- if ( !skinFile )
- {
- trap_G2API_CleanGhoul2Models( &ghoul2Ptr);
- return NULL;
- }
-
- basegroup = trap_GP_GetBaseParseGroup ( skinFile );
- group = trap_GPG_GetSubGroups ( basegroup );
-
- while(group)
- {
- trap_GPG_GetName ( group, name );
-
- // Parse the material
- if ( Q_stricmp ( name, "material") == 0)
- {
- char matName[MAX_QPATH];
- char shaderName[MAX_QPATH];
-
- trap_GPG_FindPairValue ( group, "name", "", matName );
-
- sub = trap_GPG_FindSubGroup ( group, "group");
- if (sub)
- {
- trap_GPG_FindPairValue ( sub, "shader1", "", shaderName );
- if (!shaderName[0])
- {
- trap_GPG_FindPairValue ( sub, "texture1", "", shaderName );
- }
- }
-
- if (matName[0] && shaderName[0])
- {
- end += Com_sprintf(end, sizeof(temp) - (end-temp+1), "%s %s ", matName, shaderName);
- numPairs++;
- }
- }
-
- group = trap_GPG_GetNext ( group );
- }
-
- trap_GP_Delete(&skinFile);
-
- if (numPairs)
- {
- qhandle_t handle;
-
- handle = trap_G2API_RegisterSkin( identity->mName, numPairs, temp);
- trap_G2API_SetSkin( ghoul2Ptr, 0, handle);
- }
-
- // If the gender was requested then look for female in the model
- // name as the indicator
- if ( gender )
- {
- Q_strlwr ( (char*)identity->mCharacter->mModel );
-
- *gender = GENDER_MALE;
- if ( strstr ( identity->mCharacter->mModel, "female" ) )
- {
- *gender = GENDER_FEMALE;
- }
- }
-
- return ghoul2Ptr;
- }
-